package cn.dendarii.ivan.util.file;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * <pre>
 *     author: Blankj
 *     blog  : http://blankj.com
 *     time  : 2016/8/11
 *     desc  : 文件相关工具类
 * </pre>
 */
public class FileUtils {
    private FileUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    /**
     * 根据文件路径获取文件
     * @param filePath 文件路径
     * @return 文件
     */
    public static File getFileByPath(String filePath) {
        return filePath == null ? null : new File(filePath);
    }

    /**
     * 判断文件是否存在
     * @param filePath 文件路径
     * @return {@code true}: 存在<br>
     *         {@code false}: 不存在
     */
    public static boolean isFileExists(String filePath) {
        return isFileExists(getFileByPath(filePath));
    }

    /**
     * 判断文件是否存在
     * @param file 文件
     * @return {@code true}: 存在<br>
     *         {@code false}: 不存在
     */
    public static boolean isFileExists(File file) {
        return file != null && file.exists();
    }

    /**
     * 判断是否是目录
     * @param dirPath 目录路径
     * @return {@code true}: 是<br>
     *         {@code false}: 否
     */
    public static boolean isDir(String dirPath) {
        return isDir(getFileByPath(dirPath));
    }

    /**
     * 判断是否是目录
     * @param file 文件
     * @return {@code true}: 是<br>
     *         {@code false}: 否
     */
    public static boolean isDir(File file) {
        return isFileExists(file) && file.isDirectory();
    }

    /**
     * 判断是否是文件
     * @param filePath 文件路径
     * @return {@code true}: 是<br>
     *         {@code false}: 否
     */
    public static boolean isFile(String filePath) {
        return isFile(getFileByPath(filePath));
    }

    /**
     * 判断是否是文件
     * @param file 文件
     * @return {@code true}: 是<br>
     *         {@code false}: 否
     */
    public static boolean isFile(File file) {
        return isFileExists(file) && file.isFile();
    }

    /**
     * 判断目录是否存在，不存在则判断是否创建成功
     * @param dirPath 文件路径
     * @return {@code true}: 存在或创建成功<br>
     *         {@code false}: 不存在或创建失败
     */
    public static boolean createOrExistsDir(String dirPath) {
        return createOrExistsDir(getFileByPath(dirPath));
    }

    /**
     * 判断目录是否存在，不存在则判断是否创建成功
     * @param file 文件
     * @return {@code true}: 存在或创建成功<br>
     *         {@code false}: 不存在或创建失败
     */
    public static boolean createOrExistsDir(File file) {
        // 如果存在，是目录则返回true，是文件则返回false，不存在则返回是否创建成功
        return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
    }

    /**
     * 判断文件是否存在，不存在则判断是否创建成功
     * @param filePath 文件路径
     * @return {@code true}: 存在或创建成功<br>
     *         {@code false}: 不存在或创建失败
     */
    public static boolean createOrExistsFile(String filePath) {
        return createOrExistsFile(getFileByPath(filePath));
    }

    /**
     * 判断文件是否存在，不存在则判断是否创建成功
     * @param file 文件
     * @return {@code true}: 存在或创建成功<br>
     *         {@code false}: 不存在或创建失败
     */
    public static boolean createOrExistsFile(File file) {
        if (file == null)
            return false;
        // 如果存在，是文件则返回true，是目录则返回false
        if (file.exists())
            return file.isFile();
        if (!createOrExistsDir(file.getParentFile()))
            return false;
        try {
            return file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 判断文件是否存在，存在则在创建之前删除
     * @param filePath 文件路径
     * @return {@code true}: 创建成功<br>
     *         {@code false}: 创建失败
     */
    public static boolean createFileByDeleteOldFile(String filePath) {
        return createFileByDeleteOldFile(getFileByPath(filePath));
    }

    /**
     * 判断文件是否存在，存在则在创建之前删除
     * @param file 文件
     * @return {@code true}: 创建成功<br>
     *         {@code false}: 创建失败
     */
    public static boolean createFileByDeleteOldFile(File file) {
        if (file == null)
            return false;
        // 文件存在并且删除失败返回false
        if (file.exists() && file.isFile() && !file.delete())
            return false;
        // 创建目录失败返回false
        if (!createOrExistsDir(file.getParentFile()))
            return false;
        try {
            return file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除目录
     * @param dirPath 目录路径
     * @return {@code true}: 删除成功<br>
     *         {@code false}: 删除失败
     */
    public static boolean deleteDir(String dirPath) {
        return deleteDir(getFileByPath(dirPath));
    }

    /**
     * 删除目录
     * @param dir 目录
     * @return {@code true}: 删除成功<br>
     *         {@code false}: 删除失败
     */
    public static boolean deleteDir(File dir) {
        if (dir == null)
            return false;
        // 目录不存在返回true
        if (!dir.exists())
            return true;
        // 不是目录返回false
        if (!dir.isDirectory())
            return false;
        // 现在文件存在且是文件夹
        File[] files = dir.listFiles();
        if (files != null && files.length != 0) {
            for (File file : files) {
                if (file.isFile()) {
                    if (!deleteFile(file))
                        return false;
                } else if (file.isDirectory()) {
                    if (!deleteDir(file))
                        return false;
                }
            }
        }
        return dir.delete();
    }

    /**
     * 删除文件
     * @param srcFilePath 文件路径
     * @return {@code true}: 删除成功<br>
     *         {@code false}: 删除失败
     */
    public static boolean deleteFile(String srcFilePath) {
        return deleteFile(getFileByPath(srcFilePath));
    }

    /**
     * 删除文件
     * @param file 文件
     * @return {@code true}: 删除成功<br>
     *         {@code false}: 删除失败
     */
    public static boolean deleteFile(File file) {
        return file != null && (!file.exists() || file.isFile() && file.delete());
    }

    /**
     * 删除目录下的所有文件
     * @param dirPath 目录路径
     * @return {@code true}: 删除成功<br>
     *         {@code false}: 删除失败
     */
    public static boolean deleteFilesInDir(String dirPath) {
        return deleteFilesInDir(getFileByPath(dirPath));
    }

    /**
     * 删除目录下的所有文件
     * @param dir 目录
     * @return {@code true}: 删除成功<br>
     *         {@code false}: 删除失败
     */
    public static boolean deleteFilesInDir(File dir) {
        if (dir == null)
            return false;
        // 目录不存在返回true
        if (!dir.exists())
            return true;
        // 不是目录返回false
        if (!dir.isDirectory())
            return false;
        // 现在文件存在且是文件夹
        File[] files = dir.listFiles();
        if (files != null && files.length != 0) {
            for (File file : files) {
                if (file.isFile()) {
                    if (!deleteFile(file))
                        return false;
                } else if (file.isDirectory()) {
                    if (!deleteDir(file))
                        return false;
                }
            }
        }
        return true;
    }

    /**
     * 获取目录下所有文件
     * @param dirPath 目录路径
     * @param isRecursive 是否递归进子目录
     * @return 文件链表
     */
    public static List<File> listFilesInDir(String dirPath,
                                            boolean isRecursive) {
        return listFilesInDir(getFileByPath(dirPath), isRecursive);
    }

    /**
     * 获取目录下所有文件
     * @param dir 目录
     * @param isRecursive 是否递归进子目录
     * @return 文件链表
     */
    public static List<File> listFilesInDir(File dir,
                                            boolean isRecursive) {
        if (isRecursive)
            return listFilesInDir(dir);
        if (dir == null || !isDir(dir))
            return null;
        List<File> list = new ArrayList<>();
        Collections.addAll(list, dir.listFiles());
        return list;
    }

    /**
     * 获取目录下所有文件包括子目录
     * @param dirPath 目录路径
     * @return 文件链表
     */
    public static List<File> listFilesInDir(String dirPath) {
        return listFilesInDir(getFileByPath(dirPath));
    }

    /**
     * 获取目录下所有文件包括子目录
     * @param dir 目录
     * @return 文件链表
     */
    public static List<File> listFilesInDir(File dir) {
        if (dir == null || !isDir(dir))
            return null;
        List<File> list = new ArrayList<>();
        File[] files = dir.listFiles();
        if (files != null && files.length != 0) {
            for (File file : files) {
                list.add(file);
                if (file.isDirectory()) {
                    list.addAll(listFilesInDir(file));
                }
            }
        }
        return list;
    }

    /**
     * 获取目录下所有后缀名为suffix的文件
     * <p>
     * 大小写忽略
     * </p>
     * @param dirPath 目录路径
     * @param suffix 后缀名
     * @param isRecursive 是否递归进子目录
     * @return 文件链表
     */
    public static List<File> listFilesInDirWithFilter(String dirPath,
                                                      String suffix,
                                                      boolean isRecursive) {
        return listFilesInDirWithFilter(getFileByPath(dirPath), suffix, isRecursive);
    }

    /**
     * 获取目录下所有后缀名为suffix的文件
     * <p>
     * 大小写忽略
     * </p>
     * @param dir 目录
     * @param suffix 后缀名
     * @param isRecursive 是否递归进子目录
     * @return 文件链表
     */
    public static List<File> listFilesInDirWithFilter(File dir,
                                                      String suffix,
                                                      boolean isRecursive) {
        if (isRecursive)
            return listFilesInDirWithFilter(dir, suffix);
        if (dir == null || !isDir(dir))
            return null;
        List<File> list = new ArrayList<>();
        File[] files = dir.listFiles();
        if (files != null && files.length != 0) {
            for (File file : files) {
                if (file.getName().toUpperCase().endsWith(suffix.toUpperCase())) {
                    list.add(file);
                }
            }
        }
        return list;
    }

    /**
     * 获取目录下所有后缀名为suffix的文件包括子目录
     * <p>
     * 大小写忽略
     * </p>
     * @param dirPath 目录路径
     * @param suffix 后缀名
     * @return 文件链表
     */
    public static List<File> listFilesInDirWithFilter(String dirPath,
                                                      String suffix) {
        return listFilesInDirWithFilter(getFileByPath(dirPath), suffix);
    }

    /**
     * 获取目录下所有后缀名为suffix的文件包括子目录
     * <p>
     * 大小写忽略
     * </p>
     * @param dir 目录
     * @param suffix 后缀名
     * @return 文件链表
     */
    public static List<File> listFilesInDirWithFilter(File dir,
                                                      String suffix) {
        if (dir == null || !isDir(dir))
            return null;
        List<File> list = new ArrayList<>();
        File[] files = dir.listFiles();
        if (files != null && files.length != 0) {
            for (File file : files) {
                if (file.getName().toUpperCase().endsWith(suffix.toUpperCase())) {
                    list.add(file);
                }
                if (file.isDirectory()) {
                    list.addAll(listFilesInDirWithFilter(file, suffix));
                }
            }
        }
        return list;
    }

    /**
     * 获取目录下所有符合filter的文件
     * @param dirPath 目录路径
     * @param filter 过滤器
     * @param isRecursive 是否递归进子目录
     * @return 文件链表
     */
    public static List<File> listFilesInDirWithFilter(String dirPath,
                                                      FilenameFilter filter,
                                                      boolean isRecursive) {
        return listFilesInDirWithFilter(getFileByPath(dirPath), filter, isRecursive);
    }

    /**
     * 获取目录下所有符合filter的文件
     * @param dir 目录
     * @param filter 过滤器
     * @param isRecursive 是否递归进子目录
     * @return 文件链表
     */
    public static List<File> listFilesInDirWithFilter(File dir,
                                                      FilenameFilter filter,
                                                      boolean isRecursive) {
        if (isRecursive)
            return listFilesInDirWithFilter(dir, filter);
        if (dir == null || !isDir(dir))
            return null;
        List<File> list = new ArrayList<>();
        File[] files = dir.listFiles();
        if (files != null && files.length != 0) {
            for (File file : files) {
                if (filter.accept(file.getParentFile(), file.getName())) {
                    list.add(file);
                }
            }
        }
        return list;
    }

    /**
     * 获取目录下所有符合filter的文件包括子目录
     * @param dirPath 目录路径
     * @param filter 过滤器
     * @return 文件链表
     */
    public static List<File> listFilesInDirWithFilter(String dirPath,
                                                      FilenameFilter filter) {
        return listFilesInDirWithFilter(getFileByPath(dirPath), filter);
    }

    /**
     * 获取目录下所有符合filter的文件包括子目录
     * @param dir 目录
     * @param filter 过滤器
     * @return 文件链表
     */
    public static List<File> listFilesInDirWithFilter(File dir,
                                                      FilenameFilter filter) {
        if (dir == null || !isDir(dir))
            return null;
        List<File> list = new ArrayList<>();
        File[] files = dir.listFiles();
        if (files != null && files.length != 0) {
            for (File file : files) {
                if (filter.accept(file.getParentFile(), file.getName())) {
                    list.add(file);
                }
                if (file.isDirectory()) {
                    list.addAll(listFilesInDirWithFilter(file, filter));
                }
            }
        }
        return list;
    }

    /**
     * 获取目录下指定文件名的文件包括子目录
     * <p>
     * 大小写忽略
     * </p>
     * @param dirPath 目录路径
     * @param fileName 文件名
     * @return 文件链表
     */
    public static List<File> searchFileInDir(String dirPath,
                                             String fileName) {
        return searchFileInDir(getFileByPath(dirPath), fileName);
    }

    /**
     * 获取目录下指定文件名的文件包括子目录
     * <p>
     * 大小写忽略
     * </p>
     * @param dir 目录
     * @param fileName 文件名
     * @return 文件链表
     */
    public static List<File> searchFileInDir(File dir,
                                             String fileName) {
        if (dir == null || !isDir(dir))
            return null;
        List<File> list = new ArrayList<>();
        File[] files = dir.listFiles();
        if (files != null && files.length != 0) {
            for (File file : files) {
                if (file.getName().toUpperCase().equals(fileName.toUpperCase())) {
                    list.add(file);
                }
                if (file.isDirectory()) {
                    list.addAll(searchFileInDir(file, fileName));
                }
            }
        }
        return list;
    }
    /**
     * 将输入流写入文件
     * @param filePath 路径
     * @param is 输入流
     * @param append 是否追加在文件末
     * @return {@code true}: 写入成功<br>
     *         {@code false}: 写入失败
     */
    // public static boolean writeFileFromIS(String filePath,
    // InputStream is,
    // boolean append) {
    // return writeFileFromIS(getFileByPath(filePath), is, append);
    // }

    /**
     * 将输入流写入文件
     * @param file 文件
     * @param is 输入流
     * @param append 是否追加在文件末
     * @return {@code true}: 写入成功<br>
     *         {@code false}: 写入失败
     */
    // public static boolean writeFileFromIS(File file,
    // InputStream is,
    // boolean append) {
    // if (file == null || is == null)
    // return false;
    // if (!createOrExistsFile(file))
    // return false;
    // OutputStream os = null;
    // try {
    // os = new BufferedOutputStream(new FileOutputStream(file, append));
    // byte data[] = new byte[KB];
    // int len;
    // while ((len = is.read(data, 0, KB)) != -1) {
    // os.write(data, 0, len);
    // }
    // return true;
    // } catch (IOException e) {
    // e.printStackTrace();
    // return false;
    // } finally {
    // closeIO(is, os);
    // }
    // }
    /**
     * 将字符串写入文件
     * @param filePath 文件路径
     * @param content 写入内容
     * @param append 是否追加在文件末
     * @return {@code true}: 写入成功<br>
     *         {@code false}: 写入失败
     */
    public static boolean writeFileFromString(String filePath,
                                              String content,
                                              boolean append) {
        return writeFileFromString(getFileByPath(filePath), content, append);
    }

    /**
     * 将字符串写入文件
     * @param file 文件
     * @param content 写入内容
     * @param append 是否追加在文件末
     * @return {@code true}: 写入成功<br>
     *         {@code false}: 写入失败
     */
    public static boolean writeFileFromString(File file,
                                              String content,
                                              boolean append) {
        if (file == null || content == null)
            return false;
        if (!createOrExistsFile(file))
            return false;
        FileWriter fileWriter = null;
        try {
            fileWriter = new FileWriter(file, append);
            fileWriter.write(content);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            closeIO(fileWriter);
        }
    }

    /**
     * 指定编码按行读取文件到List
     * @param filePath 文件路径
     * @param charsetName 编码格式
     * @return 文件行链表
     */
    public static List<String> readFile2List(String filePath,
                                             String charsetName) {
        return readFile2List(getFileByPath(filePath), charsetName);
    }

    /**
     * 指定编码按行读取文件到List
     * @param file 文件
     * @param charsetName 编码格式
     * @return 文件行链表
     */
    public static List<String> readFile2List(File file,
                                             String charsetName) {
        return readFile2List(file, 0, 0x7FFFFFFF, charsetName);
    }

    /**
     * 指定编码按行读取文件到List
     * @param filePath 文件路径
     * @param st 需要读取的开始行数
     * @param end 需要读取的结束行数
     * @param charsetName 编码格式
     * @return 包含制定行的list
     */
    public static List<String> readFile2List(String filePath,
                                             int st,
                                             int end,
                                             String charsetName) {
        return readFile2List(getFileByPath(filePath), st, end, charsetName);
    }

    /**
     * 指定编码按行读取文件到List
     * @param file 文件
     * @param st 需要读取的开始行数
     * @param end 需要读取的结束行数
     * @param charsetName 编码格式
     * @return 包含从start行到end行的list
     */
    public static List<String> readFile2List(File file,
                                             int st,
                                             int end,
                                             String charsetName) {
        if (file == null)
            return null;
        if (st > end)
            return null;
        BufferedReader reader = null;
        try {
            String line;
            int curLine = 1;
            List<String> list = new ArrayList<>();
            if (charsetName == null) {
                reader = new BufferedReader(new FileReader(file));
            } else {
                reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),
                                                                  charsetName));
            }
            while ((line = reader.readLine()) != null) {
                if (curLine > end)
                    break;
                if (st <= curLine && curLine <= end)
                    list.add(line);
                ++curLine;
            }
            return list;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            closeIO(reader);
        }
    }

    /**
     * 指定编码按行读取文件到字符串中
     * @param filePath 文件路径
     * @param charsetName 编码格式
     * @return 字符串
     */
    public static String readFile2String(String filePath,
                                         String charsetName) {
        return readFile2String(getFileByPath(filePath), charsetName);
    }

    /**
     * 指定编码按行读取文件到字符串中
     * @param file 文件
     * @param charsetName 编码格式
     * @return 字符串
     */
    public static String readFile2String(File file,
                                         String charsetName) {
        if (file == null)
            return null;
        BufferedReader reader = null;
        try {
            StringBuilder sb = new StringBuilder();
            if (charsetName == null) {
                reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
            } else {
                reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),
                                                                  charsetName));
            }
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line).append("\r\n");// windows系统换行为\r\n，Linux为\n
            }
            // 要去除最后的换行符
            return sb.delete(sb.length() - 2, sb.length()).toString();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            closeIO(reader);
        }
    }

    /**
     * 简单获取文件编码格式
     * @param filePath 文件路径
     * @return 文件编码
     */
    public static String getFileCharsetSimple(String filePath) {
        return getFileCharsetSimple(getFileByPath(filePath));
    }

    /**
     * 简单获取文件编码格式
     * @param file 文件
     * @return 文件编码
     */
    public static String getFileCharsetSimple(File file) {
        int p = 0;
        InputStream is = null;
        try {
            is = new BufferedInputStream(new FileInputStream(file));
            p = (is.read() << 8) + is.read();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeIO(is);
        }
        switch (p) {
        case 0xefbb:
            return "UTF-8";
        case 0xfffe:
            return "Unicode";
        case 0xfeff:
            return "UTF-16BE";
        default:
            return "GBK";
        }
    }

    /**
     * 关闭IO
     * @param closeables closeable
     */
    public static void closeIO(Closeable... closeables) {
        if (closeables == null)
            return;
        try {
            for (Closeable closeable : closeables) {
                if (closeable != null) {
                    closeable.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取全路径中的最长目录
     * @param file 文件
     * @return filePath最长目录
     */
    public static String getDirName(File file) {
        if (file == null)
            return null;
        return getDirName(file.getPath());
    }

    /**
     * 获取全路径中的最长目录
     * @param filePath 文件路径
     * @return filePath最长目录
     */
    public static String getDirName(String filePath) {
        if (filePath == null)
            return filePath;
        int lastSep = filePath.lastIndexOf(File.separator);
        return lastSep == -1 ? "" : filePath.substring(0, lastSep + 1);
    }

    /**
     * 获取全路径中的文件名
     * @param file 文件
     * @return 文件名
     */
    public static String getFileName(File file) {
        if (file == null)
            return null;
        return getFileName(file.getPath());
    }

    /**
     * 获取全路径中的文件名
     * @param filePath 文件路径
     * @return 文件名
     */
    public static String getFileName(String filePath) {
        if (filePath == null)
            return filePath;
        int lastSep = filePath.lastIndexOf(File.separator);
        return lastSep == -1 ? filePath : filePath.substring(lastSep + 1);
    }

    /**
     * 获取全路径中的不带拓展名的文件名
     * @param file 文件
     * @return 不带拓展名的文件名
     */
    public static String getFileNameNoExtension(File file) {
        if (file == null)
            return null;
        return getFileNameNoExtension(file.getPath());
    }

    /**
     * 获取全路径中的不带拓展名的文件名
     * @param filePath 文件路径
     * @return 不带拓展名的文件名
     */
    public static String getFileNameNoExtension(String filePath) {
        if (filePath == null)
            return filePath;
        int lastPoi = filePath.lastIndexOf('.');
        int lastSep = filePath.lastIndexOf(File.separator);
        if (lastSep == -1) {
            return (lastPoi == -1 ? filePath : filePath.substring(0, lastPoi));
        }
        if (lastPoi == -1 || lastSep > lastPoi) {
            return filePath.substring(lastSep + 1);
        }
        return filePath.substring(lastSep + 1, lastPoi);
    }

    /**
     * 获取全路径中的文件拓展名
     * @param file 文件
     * @return 文件拓展名
     */
    public static String getFileExtension(File file) {
        if (file == null)
            return null;
        return getFileExtension(file.getPath());
    }

    /**
     * 获取全路径中的文件拓展名 F
     * @param filePath 文件路径
     * @return 文件拓展名
     */
    public static String getFileExtension(String filePath) {
        if (filePath == null)
            return filePath;
        int lastPoi = filePath.lastIndexOf('.');
        int lastSep = filePath.lastIndexOf(File.separator);
        if (lastPoi == -1 || lastSep >= lastPoi)
            return "";
        return filePath.substring(lastPoi + 1);
    }
}
